package com.hanxiaozhang.base.util;


import cn.hutool.extra.template.*;
import com.hanxiaozhang.base.dto.ColumnDTO;
import com.hanxiaozhang.base.dto.TableDTO;
import com.hanxiaozhang.base.entity.ColumnEntity;
import com.hanxiaozhang.base.entity.TableEntity;
import com.hanxiaozhang.config.GeneratorProperties;
import com.hanxiaozhang.exception.InternalServerException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.time.LocalDate;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 〈一句话功能简述〉<br>
 * 〈代码生成工具〉
 *
 * @author hanxinghua
 * @create 2021/4/24
 * @since 1.0.0
 */
public class GeneratorUtil {


    private static Pattern pattern = Pattern.compile("_(\\w)");


    /**
     * 生成代码器
     *
     * @param table
     * @param columns
     * @param generatorProperties
     * @param zip
     */
    public static void generatorCode(TableEntity table, List<ColumnEntity> columns, GeneratorProperties generatorProperties, ZipOutputStream zip) {

        boolean isBigDecimal = false, isLocalDate = false, isLocalTime = false, isLocalDateTime = false;

        // 表名转换成Java类名
        String className = getClassName(table.getTableName(), generatorProperties.getTablePrefix(), generatorProperties.getRemoveTablePrefix());

        // 表信息
        TableDTO tableDTO = new TableDTO();
        tableDTO.setClassName(className)
                .setClassNameLower(StringUtils.uncapitalize(className))
                .setTableName(table.getTableName())
                .setComment(table.getComment());

        // 列信息
        List<ColumnDTO> columnList = new ArrayList<>();
        if (!columns.isEmpty()) {
            for (ColumnEntity column : columns) {
                ColumnDTO columnDTO = new ColumnDTO();
                // 获取属性名称
                String attributeName = getAttributeName(column.getColumnName());
                // 列的数据类型转换
                String attributeType = generatorProperties.getDbToJavaType().getOrDefault(column.getDataType(), "unknowType");

                columnDTO.setAttributeName(attributeName)
                        .setAttributeNameLower(StringUtils.uncapitalize(attributeName))
                        .setAttributeType(attributeType)
                        .setColumnName(column.getColumnName())
                        .setComment(column.getComment())
                        .setDataType(column.getDataType())
                        .setExtra(column.getExtra());

                // 是否主键
                if ("PRI".equalsIgnoreCase(column.getColumnKey()) && tableDTO.getPrimaryKey() == null) {
                    tableDTO.setPrimaryKey(columnDTO);
                }
                if ("decimal".equals(columnDTO.getDataType())) {
                    isBigDecimal = true;
                }
                if ("date".equals(columnDTO.getDataType())) {
                    isLocalDate = true;
                }
                if ("time".equals(columnDTO.getDataType())) {
                    isLocalTime = true;
                }
                if ("datetime".equals(columnDTO.getDataType()) || "timestamp".equals(columnDTO.getDataType())) {
                    isLocalDateTime = true;
                }
                columnList.add(columnDTO);
            }
        }
        tableDTO.setColumns(columnList);

        if (tableDTO.getPrimaryKey() == null) {
            tableDTO.setPrimaryKey(tableDTO.getColumns().get(0));
        }

        // 封装模板数据
        Map<String, Object> map = setTemplateMap(tableDTO, generatorProperties, isBigDecimal, isLocalDate, isLocalTime, isLocalDateTime);
        // 获取模板名称列表
        List<String> templateNames = getTemplateNames();
        // 创建引擎
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("generator", TemplateConfig.ResourceMode.CLASSPATH));

        for (String templateName : templateNames) {
            String zipName = getFileName(templateName, tableDTO.getClassName(), getPathName(generatorProperties.getPackagePath()));
            templateName = "/" + templateName + ".han";
            Template template = engine.getTemplate(templateName);
            String render = template.render(map);
            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(zipName));
                IOUtils.write(render, zip, "UTF-8");
                zip.closeEntry();
            } catch (IOException e) {
                throw new InternalServerException("渲染模板失败,表名:[{}]" + tableDTO.getTableName(), e);
            }
        }

    }

    /**
     * 获取模板
     *
     * @return
     */
    public static List<String> getTemplateNames() {
        List<String> templateNames = new ArrayList<String>();
        templateNames.add("Controller");
        templateNames.add("Service");
        templateNames.add("ServiceImpl");
        templateNames.add("Dao");
        templateNames.add("Mapper");
        templateNames.add("Entity");
        return templateNames;
    }


    /**
     * 封装模板Map
     *
     * @param tableDTO
     * @param generatorProperties
     * @param isBigDecimal
     * @param isLocalDate
     * @param isLocalTime
     * @param isLocalDateTime
     * @return
     */
    private static Map<String, Object> setTemplateMap(TableDTO tableDTO, GeneratorProperties generatorProperties, boolean isBigDecimal, boolean isLocalDate, boolean isLocalTime, boolean isLocalDateTime) {

        Map<String, Object> map = new HashMap<>(32);
        map.put("tableName", tableDTO.getTableName());
        map.put("comment", tableDTO.getComment());
        map.put("className", tableDTO.getClassName());
        map.put("classNameLower", tableDTO.getClassNameLower());
        map.put("pathName", getPathName(generatorProperties.getPackagePath()));
        map.put("primaryKey", tableDTO.getPrimaryKey());
        map.put("columns", tableDTO.getColumns());
        map.put("package", generatorProperties.getPackagePath());
        map.put("author", generatorProperties.getAuthor());
        map.put("email", generatorProperties.getEmail());
        map.put("since", generatorProperties.getSince());
        map.put("datetime", LocalDate.now().toString());
        map.put("isBigDecimal", isBigDecimal);
        map.put("isLocalDate", isLocalDate);
        map.put("isLocalTime", isLocalTime);
        map.put("isLocalDateTime", isLocalDateTime);
        return map;
    }


    /**
     * 获取路径名称
     *
     * @param packagePath
     * @return
     */
    private static String getPathName(String packagePath) {

        return packagePath.substring(packagePath.lastIndexOf(".") + 1);
    }

    /**
     * 获取属性名称
     *
     * @param columnName
     * @return
     */
    private static String getAttributeName(String columnName) {

        return lineToHump(columnName);
    }


    /**
     * 获取类名
     *
     * @param tableName
     * @param tablePrefix
     * @param removeTablePrefix
     * @return
     */
    private static String getClassName(String tableName, String tablePrefix, Boolean removeTablePrefix) {

        if (removeTablePrefix && StringUtils.isNotBlank(tablePrefix)) {
            tableName = tableName.replace(tablePrefix, "");
        }
        return lineToHump(tableName);
    }


    /**
     * 获取文件名
     *
     * @param templateName
     * @param className
     * @param packageName
     * @return
     */
    private static String getFileName(String templateName, String className, String packageName) {

        String packageJavaPath = "main" + File.separator + "java" + File.separator;

        String packageResourcesPath = "main" + File.separator + "resources" + File.separator;


        if (StringUtils.isNotBlank(packageName)) {
            packageJavaPath += packageName.replace(".", File.separator) + File.separator;
        }

        if (templateName.equals("Controller")) {
            return packageJavaPath + "controller" + File.separator + className + "Controller.java";
        }

        if (templateName.equals("Service")) {
            return packageJavaPath + "service" + File.separator + className + "Service.java";
        }

        if (templateName.equals("ServiceImpl")) {
            return packageJavaPath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
        }

        if (templateName.equals("Dao")) {
            return packageJavaPath + "dao" + File.separator + className + "Dao.java";
        }

        if (templateName.equals("Entity")) {
            return packageJavaPath + "entity" + File.separator + className + "Entity.java";
        }

        if (templateName.equals("Mapper")) {
            return packageResourcesPath + "mapper" + File.separator + packageName + File.separator + className + "Mapper.xml";
        }

        return null;
    }


    /**
     * 下划线转驼峰
     *
     * @param str
     * @return
     */
    private static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = pattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


}
